package com.bestcem.xm.ticket.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.StringUtil;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.ticket.constant.CommonConstant;
import com.bestcem.xm.ticket.constant.Constants;
import com.bestcem.xm.ticket.controller.convert.TemplateConvert;
import com.bestcem.xm.ticket.controller.param.PageParam;
import com.bestcem.xm.ticket.controller.param.ticket.template.TemplateParam;
import com.bestcem.xm.ticket.controller.param.ticket.template.UpdateTemplateParam;
import com.bestcem.xm.ticket.controller.vo.template.GetRecentTemplateVO;
import com.bestcem.xm.ticket.controller.vo.ticket.template.GetTemplateInfoVO;
import com.bestcem.xm.ticket.controller.vo.ticket.template.GetTemplateVO;
import com.bestcem.xm.ticket.controller.vo.ticket.template.ParseCascadeExcelVO;
import com.bestcem.xm.ticket.dao.TemplateDao;
import com.bestcem.xm.ticket.dao.TicketConfigDao;
import com.bestcem.xm.ticket.entity.mongo.TicketConfig;
import com.bestcem.xm.ticket.entity.mongo.TicketTemplateV2;
import com.bestcem.xm.ticket.enums.*;
import com.bestcem.xm.ticket.grpc.client.BaseUserGrpcService;
import com.bestcem.xm.ticket.grpc.client.dto.user.UserDTO;
import com.bestcem.xm.ticket.service.TemplateService;
import com.bestcem.xm.ticket.util.TemplateCheckUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 描述信息
 *
 * @author xuan.yang <xuan.yang@idiaoyan.com>
 * @version v2.3
 * @date 2021/9/2 14:53
 */
@Service
@Slf4j
public class TemplateServiceImpl implements TemplateService {
    public static final String ILLEGAL_ARGUMENT = "参数不合法";
    @Resource
    private TemplateDao templateDao;
    @Resource
    private BaseUserGrpcService baseUserGrpcService;
    @Resource
    private TicketConfigDao ticketConfigDao;
    @Resource
    private RedisService redisService;
    @Resource
    private TemplateService templateService;

    @Override
    public ServiceResult<GetTemplateVO> listTemplate(Integer type, PageParam pageParam, String orgId, boolean listAllFlag) {
        // 获取模板count
        long total = templateDao.getTemplateCount(type, orgId);
        long absTotal = templateDao.getTemplateCount(null, orgId);
        // 查询该公司下是否有详情默认模板与处理默认模板
        PageParam defaultPageParam = new PageParam(1, 2);
        List<TicketTemplateV2> defaultTemplateV2List = templateDao.listTemplate(null, defaultPageParam, orgId, false);
        if (createFlag(defaultTemplateV2List)) {
            // 先判断该orgId下是否有默认处理/详情模板，没有的话创建出来（校验逻辑已经包装在方法中，需要加锁）
            createTicketDefaultTemplate(orgId);
            total = templateDao.getTemplateCount(type, orgId);
            absTotal = templateDao.getTemplateCount(null, orgId);
        }

        // 获取模板分页数据
        List<TicketTemplateV2> ticketTemplateV2List = templateDao.listTemplate(type, pageParam, orgId, listAllFlag);
        // 如果是分页查找的话，需要“根据type查询该id是否在工单config表中被引用”
        if (!listAllFlag) {
            ticketTemplateV2List.forEach(ticketTemplateV2 -> {
                // 根据type查询该id是否在工单config表中被引用
                String id = "";
                if (TemplateTypeEnum.TEMPLATE_INFO.getIndex().equals(ticketTemplateV2.getType())) {
                    id = ticketConfigDao.queTemplateDetailId(ticketTemplateV2.getId());
                }
                if (TemplateTypeEnum.TEMPLATE_HANDLE.getIndex().equals(ticketTemplateV2.getType())) {
                    id = ticketConfigDao.queTemplateHandleId(ticketTemplateV2.getId());
                }
                // 根据被引用的结果，set到used字段中
                if (!StringUtil.isNullOrEmpty(id)) {
                    ticketTemplateV2.setUsed(TemplateUsedEnum.TEMPLATE_USED.getIndex());
                } else {
                    ticketTemplateV2.setUsed(TemplateUsedEnum.TEMPLATE_UNUSED.getIndex());
                }
            });
        }

        GetTemplateVO getTemplateVO = new GetTemplateVO();
        getTemplateVO.addEntityToVo(ticketTemplateV2List, total, pageParam.getPage(), absTotal);
        return ServiceResult.success(getTemplateVO);
    }

    /**
     * 是否创建默认模板的flag
     *
     * @param defaultTemplateV2List 公司下默认模板
     * @return 是否创建默认模板的flag
     * @author ympan <yuming.pan@idiaoyan.com>
     * @date 10:23 2021/9/28
     */
    private boolean createFlag(List<TicketTemplateV2> defaultTemplateV2List) {
        if (CollUtil.isEmpty(defaultTemplateV2List)) {
            return true;
        }
        if (defaultTemplateV2List.size() < 2) {
            return true;
        }
        for (TicketTemplateV2 ticketTemplateV2 : defaultTemplateV2List) {
            // 获取的两个模板中有一个不为默认模板，就需要新建默认模板
            if (ticketTemplateV2.getSeq() != 0) {
                return true;
            }
        }
        return false;
    }

    @Override
    public ServiceResult<String> insertTemplate(TemplateParam templateParam, String orgId, String userId) {
        if (!ObjectId.isValid(orgId)) {
            return ServiceResult.fail("公司id格式错误");
        }
        if (!ObjectId.isValid(userId)) {
            return ServiceResult.fail("用户id错误");
        }
        ServiceResult<String> checkResult = TemplateCheckUtil.checkSaveTemplate(templateParam);
        if (!checkResult.isSuccess() && !StrUtil.equals(checkResult.getErrorCode(), "0")) {
            return ServiceResult.fail(checkResult.getMsg());
        }
        // 保证一个公司的模板不能重复
        List<TicketTemplateV2> templateV2s = templateDao.getTemplateByOrgIdAndName(orgId, templateParam.getName());
        if (CollUtil.isNotEmpty(templateV2s)) {
            return ServiceResult.fail("该公司下模板名称已经存在");
        }
        TicketTemplateV2 ticketTemplateV2 = new TicketTemplateV2();
        List<TicketTemplateV2.FieldInfo> fieldInfos = TemplateConvert.fieldInfoConvertForInsert(templateParam.getFieldList());
        ticketTemplateV2.setFieldList(fieldInfos);
        ticketTemplateV2.setName(templateParam.getName());
        ticketTemplateV2.setType(templateParam.getType());
        ticketTemplateV2.setUsed(templateParam.getUse());
        ticketTemplateV2.setOrgId(orgId);
        ticketTemplateV2.setUpdatedId(userId);
        ticketTemplateV2.setCreatedId(userId);
        UserDTO user = baseUserGrpcService.getUser(userId);
        String name = Optional.ofNullable(user).orElse(new UserDTO()).getName();
        ticketTemplateV2.setCreator(Optional.ofNullable(name).orElse(Constants.BLANK));
        TicketTemplateV2 templateV2 = templateDao.insertTemplate(ticketTemplateV2);
        return ServiceResult.success(templateV2.getId());
    }

    @Override
    public ServiceResult<GetTemplateInfoVO> getTemplateById(String templateId) {
        TicketTemplateV2 ticketTemplateV2 = templateDao.getTemplateById(templateId);
        if (Objects.isNull(ticketTemplateV2)) {
            return ServiceResult.fail("没有查到模板详情");
        }
        List<GetTemplateInfoVO.FieldInfo> fieldInfos = TemplateConvert.fieldInfoConvertForGet(ticketTemplateV2.getFieldList());
        GetTemplateInfoVO getTemplateInfoVO = new GetTemplateInfoVO();
        getTemplateInfoVO.setId(ticketTemplateV2.getId());
        getTemplateInfoVO.setName(ticketTemplateV2.getName());
        getTemplateInfoVO.setType(ticketTemplateV2.getType());
        getTemplateInfoVO.setFieldInfos(fieldInfos);
        return ServiceResult.success(getTemplateInfoVO);
    }

    @Override
    public ServiceResult<TicketTemplateV2> deleteTemplateById(String id, String orgId) {
        if (!ObjectId.isValid(orgId)) {
            return ServiceResult.fail("删除失败");
        }
        TicketTemplateV2 template = templateDao.getTemplateById(id);
        // 查询该模板是否已经在使用
        TicketConfig ticketConfig = ticketConfigDao.findByTemplateId(id, template.getType());
        if (Objects.nonNull(template) && Objects.nonNull(ticketConfig)) {
            // 更新config表中引用为默认模板
            TicketTemplateV2 templateV2 = templateDao.getDefaultTemplate(orgId, template.getType());
            // 找到默认模板的id
            if (Objects.nonNull(templateV2)) {
                // 更新模板为默认模板
                Integer count = ticketConfigDao.updateDefaultTemplateId(template.getType(), orgId, templateV2.getId(), id);
                log.info("---------更改默认模板的个数为{}---", count);
            }

        }
        Integer integer = templateDao.deleteTemplateById(id);
        return integer != 0 ? ServiceResult.success() : ServiceResult.fail("删除失败");
    }

    @Override
    public ServiceResult<String> updateTemplate(String id, UpdateTemplateParam updateTemplateParam, String orgId, String userId) {
        // 从数据库查询模板看是否存在
        TicketTemplateV2 oldTicketTemplateV2 = templateDao.getTemplateByIdAndOrgId(id, orgId);
        if (Objects.isNull(oldTicketTemplateV2)) {
            return ServiceResult.failWithCode(ResourceErrorCodeEnum.NOT_FOUND, "模板不存在");
        }
        // 保证一个公司的模板不能重复
        List<TicketTemplateV2> templateV2List = templateDao.getTemplateByOrgIdAndName(orgId, updateTemplateParam.getName());
        if (CollUtil.isNotEmpty(templateV2List)) {
            for (TicketTemplateV2 ticketTemplateV2 : templateV2List) {
                if (!StringUtils.equals(ticketTemplateV2.getId(), id)) {
                    return ServiceResult.fail("该公司下模板名称已经存在");
                }
            }
        }

        Integer flag = updateTemplateParam.getFlag();
        if (flag.equals(UpdateTemplateEnum.UPDATE_NAME_ONLY.getIndex())) {
            // 更新模板名称
            Long modifiedCount = templateDao.updateTemplateName(id, updateTemplateParam.getName(), userId);
            if (modifiedCount < 1) {
                return ServiceResult.failWithCode(ResourceErrorCodeEnum.WRITE_FAIL, "模板名称更新失败");
            }
            return ServiceResult.success(id, "模板名称更新成功");
        }

        // 根据参数生成模板信息
        TicketTemplateV2 newTicketTemplateV2 = new TicketTemplateV2();
        List<TicketTemplateV2.FieldInfo> fieldInfos = TemplateConvert.fieldInfoConvertForUpd(updateTemplateParam.getFieldList());
        newTicketTemplateV2.setId(id);
        newTicketTemplateV2.setName(updateTemplateParam.getName());
        newTicketTemplateV2.setFieldList(fieldInfos);
        newTicketTemplateV2.setUpdatedId(userId);

        List<TicketTemplateV2.FieldInfo> oldFieldList = oldTicketTemplateV2.getFieldList();
        // 将可以修改的数据移除，剩下的皆为不可修改的数据
        oldFieldList.removeIf(fieldInfo -> fieldInfo.getIsUpdate().equals(TicketTemplateFieldUpdatedEnum.TEMPLATE_IS_UPDATE.getIndex()));
        // 校验模板字段是否可以修改，如果校验通过，则表明newTicketTemplateV2的fieldList皆为可修改的字段
        for (TicketTemplateV2.FieldInfo newInfo : newTicketTemplateV2.getFieldList()) {
            for (TicketTemplateV2.FieldInfo oldInfo : oldTicketTemplateV2.getFieldList()) {
                // 传进来的参数中，含有不可修改的字段
                if (newInfo.getName().equals(oldInfo.getName())) {
                    return ServiceResult.failWithCode(ResourceErrorCodeEnum.NOT_FOUND, "模板字段不可修改");
                }
            }
        }
        // 直接更新会覆盖原来的值，保证不可修改在前面，加入到最前面，所以要把不可修改的字段加到newTicketTemplateV2的fieldList中
        newTicketTemplateV2.getFieldList().addAll(0, oldFieldList);
        Long modifiedCount = templateDao.updateTemplate(newTicketTemplateV2, true);
        if (modifiedCount < 1) {
            return ServiceResult.failWithCode(ResourceErrorCodeEnum.WRITE_FAIL, "模板更新失败");
        }
        return ServiceResult.success(id, "模板更新成功");
    }

    @Override
    public ServiceResult<GetRecentTemplateVO> getRecentUseTemplate(String orgId, String userId) {

        TicketConfig ticketConfig = ticketConfigDao.getRecentUseTemplate(orgId, userId);
        GetRecentTemplateVO newTemplateVO = new GetRecentTemplateVO();

        // 查出默认模板和最近使用模板，如果找不到最近使用，使用默认模板
        List<TicketTemplateV2> templateV2s = templateDao.getDefaultTemplate(orgId);
        if (CollectionUtil.isEmpty(templateV2s) && Objects.isNull(ticketConfig)) {
            log.info("---------查找默认模板为空----");
            return ServiceResult.fail("获取详情失败");
        }
        log.info("------获取到默认模板个数{}---", templateV2s.size());
        GetRecentTemplateVO recentTemplateVO = new GetRecentTemplateVO();
        // 可能存在多个处理模板和详情模板，找到一个即可
        if (CollectionUtil.isNotEmpty(templateV2s)) {
            for (TicketTemplateV2 ticketTemplateV2 : templateV2s) {
                if (TemplateTypeEnum.TEMPLATE_INFO.getIndex().equals(ticketTemplateV2.getType())) {
                    recentTemplateVO.setDetailName(ticketTemplateV2.getName());
                    recentTemplateVO.setDetailId(ticketTemplateV2.getId());
                } else if (TemplateTypeEnum.TEMPLATE_HANDLE.getIndex().equals(ticketTemplateV2.getType())) {
                    recentTemplateVO.setHandleId(ticketTemplateV2.getId());
                    recentTemplateVO.setHandleName(ticketTemplateV2.getName());
                } else {
                    log.info("--------默认模板类型有误{}----", ticketTemplateV2);
                }
                if (StringUtils.isNoneBlank(recentTemplateVO.getDetailName(), recentTemplateVO.getHandleName())) {
                    break;
                }
            }
        }
        BeanUtils.copyProperties(recentTemplateVO, newTemplateVO);
        if (ObjectUtil.isEmpty(ticketConfig)) {

            return ServiceResult.success(recentTemplateVO);
        }

        List<TicketTemplateV2> templateV2List = templateDao.listTemplateByIds(Arrays.asList(ticketConfig.getTicketTemplateId(), ticketConfig.getDetailTemplateId()));
        if (CollUtil.isEmpty(templateV2List)) {
            log.info("-------ticketConfig中存储的处理模板或详情模板有问题使用默认模板,ticketconfig{}----", ticketConfig);
            return ServiceResult.success(newTemplateVO);
        }
        log.info("---------获取详情模板的个数{}---", templateV2List.size());
        GetRecentTemplateVO recentVO = TemplateConvert.convertToTemplateRecentVO(templateV2List);
        if (Objects.isNull(recentVO)) {
            return ServiceResult.fail("获取详情失败");
        }
        // 将获得的包装一下，防止有模板查询不到，用默认的进行兜底
        if (StringUtils.isNotBlank(recentVO.getDetailName())) {
            newTemplateVO.setDetailId(recentVO.getDetailId());
            newTemplateVO.setDetailName(recentVO.getDetailName());
        }
        if (StringUtils.isNotBlank(recentVO.getHandleName())) {
            newTemplateVO.setHandleId(recentVO.getHandleId());
            newTemplateVO.setHandleName(recentVO.getHandleName());
        }

        return ServiceResult.success(newTemplateVO);
    }

    @Override
    @SuppressWarnings({"unchecked", "rawtypes"})
    public ServiceResult parseCascadeExcel(List<List<Object>> optionHint, List<List<Object>> rows) {
        if (Objects.isNull(rows)) {
            return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_LOST, "缺少rows参数");
        }
        if (rows.size() > CommonConstant.MAX_ROW_COUNT) {
            return ServiceResult.fail(ReqErrorCodeEnum.PARAM_RANGE, "选项总数不超过4000行");
        }

        // 第一层级Map结构
        LinkedHashMap level1 = new LinkedHashMap();
        // 最大层级
        int maxLevel = Integer.MIN_VALUE;
        ServiceResult genLevelResult = genLevel(rows, level1, maxLevel);
        if (!genLevelResult.isSuccess()) {
            return genLevelResult;
        }
        // 父id
        int pid = 0;
        // 层级
        int level = 1;
        // 返回list
        List<ParseCascadeExcelVO.Option> optionsLevel1 = new ArrayList<>();
        List<ParseCascadeExcelVO.Option> optionsLevel2 = new ArrayList<>();
        List<ParseCascadeExcelVO.Option> optionsLevel3 = new ArrayList<>();
        List<ParseCascadeExcelVO.Option> optionsLevel4 = new ArrayList<>();
        List<ParseCascadeExcelVO.Option> optionsLevel5 = new ArrayList<>();

        // “选项”id
        int[] id = new int[]{1};
        genOption(level1, id, pid, level,
                optionsLevel1,
                optionsLevel2,
                optionsLevel3,
                optionsLevel4,
                optionsLevel5);

        // 生成VO
        ParseCascadeExcelVO.Detail detail1 = new ParseCascadeExcelVO.Detail(optionsLevel1, 1);
        ParseCascadeExcelVO.Detail detail2 = new ParseCascadeExcelVO.Detail(optionsLevel2, 2);
        ParseCascadeExcelVO.Detail detail3 = new ParseCascadeExcelVO.Detail(optionsLevel3, 3);
        ParseCascadeExcelVO.Detail detail4 = new ParseCascadeExcelVO.Detail(optionsLevel4, 4);
        ParseCascadeExcelVO.Detail detail5 = new ParseCascadeExcelVO.Detail(optionsLevel5, 5);
        List<ParseCascadeExcelVO.Detail> details = new ArrayList<>();
        details.add(detail1);
        details.add(detail2);
        details.add(detail3);
        details.add(detail4);
        details.add(detail5);
        ParseCascadeExcelVO parseCascadeExcelVO = new ParseCascadeExcelVO(details);
        return ServiceResult.success(parseCascadeExcelVO);
    }


    /**
     * 根据解析后的“选项”行数据，生成Map层级结构，
     *
     * @param rows     “选项”行数据
     * @param level1   第一层级Map结构
     * @param maxLevel 最大层级
     * @return 返回最大层级
     * @author ympan <yuming.pan@idiaoyan.com>
     * @date 15:12 2021/9/6
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private ServiceResult genLevel(List<List<Object>> rows, LinkedHashMap level1, int maxLevel) {
        for (List<Object> row : rows) {
            maxLevel = Math.max(maxLevel, row.size());
            for (int j = 0; j < row.size(); j++) {
                if (row.size() > CommonConstant.MAX_LEVEL_COUNT) {
                    return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, "层级最大支持五级");
                }
                String item = (String) row.get(j);
                if (!StringUtil.isNullOrEmpty(item)) {
                    if (item.length() > CommonConstant.MAX_CHARACTER_LENGTH_TWENTY) {
                        return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, "每个选项字数不超过20字");
                    }
                    LinkedHashMap level2;
                    LinkedHashMap level3;
                    LinkedHashMap level4;
                    LinkedHashMap level5;
                    switch (j) {
                        case 0:
                            if (level1.containsKey(item)) {
                                continue;
                            }
                            level1.put(item, new LinkedHashMap());
                            if (level1.size() > CommonConstant.MAX_ITEM_COUNT_PER_LEVEL) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.MAX_ITEM_COUNT_PER_LEVEL_MSG);
                            }
                            break;
                        case 1:
                            level2 = (LinkedHashMap) level1.get(row.get(j - 1));
                            if (Objects.isNull(level2)) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.DO_NOT_SKIP_CONTENT);
                            }
                            if (level2.containsKey(item)) {
                                continue;
                            }
                            level2.put(item, new LinkedHashMap());
                            if (level2.size() > CommonConstant.MAX_ITEM_COUNT_PER_LEVEL) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.MAX_ITEM_COUNT_PER_LEVEL_MSG);
                            }
                            break;
                        case 2:
                            level2 = (LinkedHashMap) level1.get(row.get(j - 2));
                            level3 = (LinkedHashMap) level2.get(row.get(j - 1));
                            if (Objects.isNull(level3)) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.DO_NOT_SKIP_CONTENT);
                            }
                            if (level3.containsKey(item)) {
                                continue;
                            }
                            level3.put(item, new LinkedHashMap());
                            if (level3.size() > CommonConstant.MAX_ITEM_COUNT_PER_LEVEL) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.MAX_ITEM_COUNT_PER_LEVEL_MSG);
                            }
                            break;
                        case 3:
                            level2 = (LinkedHashMap) level1.get(row.get(j - 3));
                            level3 = (LinkedHashMap) level2.get(row.get(j - 2));
                            level4 = (LinkedHashMap) level3.get(row.get(j - 1));
                            if (Objects.isNull(level4)) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.DO_NOT_SKIP_CONTENT);
                            }
                            if (level4.containsKey(item)) {
                                continue;
                            }
                            level4.put(item, new LinkedHashMap());
                            if (level4.size() > CommonConstant.MAX_ITEM_COUNT_PER_LEVEL) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.MAX_ITEM_COUNT_PER_LEVEL_MSG);
                            }
                            break;
                        case 4:
                            level2 = (LinkedHashMap) level1.get(row.get(j - 4));
                            level3 = (LinkedHashMap) level2.get(row.get(j - 3));
                            level4 = (LinkedHashMap) level3.get(row.get(j - 2));
                            level5 = (LinkedHashMap) level4.get(row.get(j - 1));
                            if (Objects.isNull(level5)) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.DO_NOT_SKIP_CONTENT);
                            }
                            if (level5.containsKey(item)) {
                                continue;
                            }
                            level5.put(item, null);
                            if (level5.size() > CommonConstant.MAX_ITEM_COUNT_PER_LEVEL) {
                                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_RANGE, CommonConstant.MAX_ITEM_COUNT_PER_LEVEL_MSG);
                            }
                            break;
                        default:
                            // 层级最大支持五级，之前已经做过判断
                    }
                }
            }
        }
        return ServiceResult.success(maxLevel);
    }

    /**
     * 根据解析后的“选项”层级map数据，生成ParseCascadeExcelVO对象并set到返回的list中
     *
     * @param lhm   所在层级map结构
     * @param idArr “选项”id
     * @param pid   父id
     * @param level 所在层级
     * @author ympan <yuming.pan@idiaoyan.com>
     * @date 15:17 2021/9/6
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public void genOption(Map<String, LinkedHashMap> lhm, int[] idArr, int pid, int level,
                          List<ParseCascadeExcelVO.Option> optionsLevel1,
                          List<ParseCascadeExcelVO.Option> optionsLevel2,
                          List<ParseCascadeExcelVO.Option> optionsLevel3,
                          List<ParseCascadeExcelVO.Option> optionsLevel4,
                          List<ParseCascadeExcelVO.Option> optionsLevel5) {
        for (Map.Entry<String, LinkedHashMap> entry : lhm.entrySet()) {
            int id = idArr[0];
            ParseCascadeExcelVO.Option option = new ParseCascadeExcelVO.Option(id, pid, entry.getKey());
            idArr[0] = id + 1;
            setOptionIntoListByLevel(option, level, optionsLevel1, optionsLevel2, optionsLevel3, optionsLevel4, optionsLevel5);
            // 下探到第五层，直接返回即可
            if (entry.getValue() == null) {
                continue;
            }
            genOption(entry.getValue(), idArr, id, level + 1,
                    optionsLevel1,
                    optionsLevel2,
                    optionsLevel3,
                    optionsLevel4,
                    optionsLevel5);
        }
    }

    /**
     * 将option设置到相应层级的列表中
     *
     * @param option        级联选项
     * @param level         级联层级
     * @param optionsLevel1 第1层级的option列表
     * @param optionsLevel2 第2层级的option列表
     * @param optionsLevel3 第3层级的option列表
     * @param optionsLevel4 第4层级的option列表
     * @param optionsLevel5 第5层级的option列表
     * @author ympan <yuming.pan@idiaoyan.com>
     * @date 23:05 2021/9/9
     */
    private void setOptionIntoListByLevel(ParseCascadeExcelVO.Option option, int level, List<ParseCascadeExcelVO.Option> optionsLevel1, List<ParseCascadeExcelVO.Option> optionsLevel2, List<ParseCascadeExcelVO.Option> optionsLevel3, List<ParseCascadeExcelVO.Option> optionsLevel4, List<ParseCascadeExcelVO.Option> optionsLevel5) {
        switch (level) {
            case 1:
                optionsLevel1.add(option);
                // 该break不影响外层for循环
                break;
            case 2:
                optionsLevel2.add(option);
                break;
            case 3:
                optionsLevel3.add(option);
                break;
            case 4:
                optionsLevel4.add(option);
                break;
            case 5:
                optionsLevel5.add(option);
                break;
            default:
                // 该分支不会进入
                break;
        }
    }

    @Override
    public void createTicketDefaultTemplate(String orgId) {
        if (Validator.isEmpty(orgId)) {
            log.error("[Ticket] 创建工单默认模板失败，没有传递orgId");
            return;
        }
        if (!ObjectId.isValid(orgId)) {
            log.error("[Ticket] 创建工单默认模板失败，orgId={},格式校验失败", orgId);
            return;
        }
        // 默认模板为null，加锁防止并发地新增
        String key = "ticket:template:" + orgId;
        RLock lock = this.redisService.getLock(key);
        // 加锁成功与否
        try {
            // 加锁, 10S 后释放
            lock.lock(10L, TimeUnit.SECONDS);
            List<TicketTemplateV2> templateV2List = new ArrayList<>();
            TicketTemplateV2 detailTemplate = templateDao.getTemplateByName(orgId, Constants.TICKET_DETAIL_DEFAULT_TEMPLATE_NAME);
            if (Objects.isNull(detailTemplate)) {
                templateV2List.add(getDefaultDetailTemplateParam(orgId));
            }
            TicketTemplateV2 handleTemplate = templateDao.getTemplateByName(orgId, Constants.TICKET_HANDLE_TEMPLATE_DEFAULT_NAME);
            if (Objects.isNull(handleTemplate)) {
                templateV2List.add(getDefaultHandleTemplateParam(orgId));
            }
            templateDao.batchInsertTemplate(templateV2List);
        } finally {
            // 是否当前线程持有锁
            if (lock.isHeldByCurrentThread()) {
                // 手动释放锁
                lock.unlock();
            }
        }
    }

    /**
     * 获取默认模板参数
     *
     * @param orgId 组织id
     * @return TicketTemplateV2
     * @author jy.zhao
     * @date 2021/11/10 16:58
     **/
    private TicketTemplateV2 getDefaultHandleTemplateParam(String orgId) {
        // 保存默认的处理模板的主要逻辑
        TicketTemplateV2 ticketTemplateV2 = new TicketTemplateV2();
        ticketTemplateV2.setOrgId(orgId);
        ticketTemplateV2.setName(Constants.TICKET_HANDLE_TEMPLATE_DEFAULT_NAME);
        ticketTemplateV2.setType(TemplateTypeEnum.TEMPLATE_HANDLE.getIndex());
        ticketTemplateV2.setUsed(2);
        // 默认模板序号为0
        ticketTemplateV2.setSeq(Constants.DEFAULT_TEMPLATE_SEQ);
        // 相关客户模板字段
        TicketTemplateV2.FieldInfo fieldInfo = new TicketTemplateV2.FieldInfo();
        fieldInfo.setName("处理记录");
        // 前端需要，使该字段在所有字段前面显示，从field1开始增长，但处理模板的默认字段固定为field0
        fieldInfo.setField("field0");
        // 默认处理模板的该字段，required为true，是必填的
        fieldInfo.setRequired(true);
        fieldInfo.setIsUpdate(TicketTemplateFieldUpdatedEnum.TEMPLATE_NOT_UPDATE.getIndex());
        fieldInfo.setType(TemplateFieldTypeEnum.MULTI_LINE.getIndex());
        ticketTemplateV2.setFieldList(Collections.singletonList(fieldInfo));
        ticketTemplateV2.setCreator("系统");
        ticketTemplateV2.setCreatedDate(DateUtil.getMongoDate());
        return ticketTemplateV2;
    }

    private TicketTemplateV2 getDefaultDetailTemplateParam(String orgId) {
        // 保存默认的详情模板的主要逻辑
        TicketTemplateV2 ticketTemplateV2 = new TicketTemplateV2();
        ticketTemplateV2.setOrgId(orgId);
        ticketTemplateV2.setName(Constants.TICKET_DETAIL_DEFAULT_TEMPLATE_NAME);
        ticketTemplateV2.setType(TemplateTypeEnum.TEMPLATE_INFO.getIndex());
        ticketTemplateV2.setUsed(2);
        // 默认模板序号为0
        ticketTemplateV2.setSeq(Constants.DEFAULT_TEMPLATE_SEQ);
        List<TicketTemplateV2.FieldInfo> fieldInfoList = new ArrayList<>();
        // 相关客户模板字段
        TicketTemplateV2.FieldInfo fileInfo = new TicketTemplateV2.FieldInfo();
        fileInfo.setName("相关客户");
        // 该字段可以修改
        fileInfo.setIsUpdate(TicketTemplateFieldUpdatedEnum.TEMPLATE_IS_UPDATE.getIndex());
        fileInfo.setType(TemplateFieldTypeEnum.SELECT.getIndex());
        fileInfo.setRequired(false);
        // 从field1开始增长，但处理模板的默认字段固定为field0
        fileInfo.setField("field1");
        // 单选的选项值
        TicketTemplateV2.SingleSelect singleSelect = new TicketTemplateV2.SingleSelect();
        singleSelect.setSource(TemplateSelectSourceEnum.SYSTEM_MEMBER.getIndex());
        singleSelect.setContent("联系人");
        // 详情描述模板字段
        fileInfo.setSingleSelect(singleSelect);
        TicketTemplateV2.FieldInfo fileInfo2 = new TicketTemplateV2.FieldInfo();
        fileInfo2.setName("详情描述");
        fileInfo2.setType(TemplateFieldTypeEnum.MULTI_LINE.getIndex());
        // 是否必填
        fileInfo2.setRequired(false);
        // 该字段可以修改
        fileInfo2.setIsUpdate(TicketTemplateFieldUpdatedEnum.TEMPLATE_IS_UPDATE.getIndex());
        // 从field1开始增长，但处理模板的默认字段固定为field0
        fileInfo2.setField("field2");
        fieldInfoList.add(fileInfo);
        fieldInfoList.add(fileInfo2);
        ticketTemplateV2.setFieldList(fieldInfoList);
        ticketTemplateV2.setCreator("系统");
        ticketTemplateV2.setCreatedDate(DateUtil.getMongoDate());
        return ticketTemplateV2;
    }

    /**
     * 保存默认处理模板
     *
     * @param orgId 组织id
     * @author jy.zhao
     * @date 2021/9/7 13:31
     **/
    private void saveDefaultHandleTemplate(String orgId) {
        TicketTemplateV2 handleTemplate = templateDao.getTemplateByName(orgId, Constants.TICKET_HANDLE_TEMPLATE_DEFAULT_NAME);
        if (Validator.isNotNull(handleTemplate)) {
            log.info("[ticket] 组织orgId={}下已有默认处理模板", orgId);
            return;
        }

        // 默认模板为null，加锁防止并发地新增
        String key = "ticket:template:" + orgId;
        RLock lock = this.redisService.getLock(key);
        // 加锁成功与否
        try {
            // 加锁, 不自动释放
            lock.lock(10L, TimeUnit.HOURS);
            handleTemplate = templateDao.getTemplateByName(orgId, Constants.TICKET_HANDLE_TEMPLATE_DEFAULT_NAME);
            if (Validator.isNotNull(handleTemplate)) {
                log.info("[ticket] 组织orgId={}下已有默认详情模板", orgId);
                return;
            }

            // 保存默认的处理模板的主要逻辑
            TicketTemplateV2 ticketTemplateV2 = new TicketTemplateV2();
            ticketTemplateV2.setOrgId(orgId);
            ticketTemplateV2.setName(Constants.TICKET_HANDLE_TEMPLATE_DEFAULT_NAME);
            ticketTemplateV2.setType(TemplateTypeEnum.TEMPLATE_HANDLE.getIndex());
            ticketTemplateV2.setUsed(1);
            // 默认模板序号为0
            ticketTemplateV2.setSeq(0);
            // 相关客户模板字段
            TicketTemplateV2.FieldInfo fieldInfo = new TicketTemplateV2.FieldInfo();
            fieldInfo.setName("处理记录");
            // 前端需要，使该字段在所有字段前面显示，从field1开始增长，但处理模板的默认字段固定为field0
            fieldInfo.setField("field0");
            // 默认处理模板的该字段，required为true，是必填的
            fieldInfo.setRequired(true);
            fieldInfo.setIsUpdate(TicketTemplateFieldUpdatedEnum.TEMPLATE_NOT_UPDATE.getIndex());
            fieldInfo.setType(TemplateFieldTypeEnum.MULTI_LINE.getIndex());
            ticketTemplateV2.setFieldList(Collections.singletonList(fieldInfo));
            ticketTemplateV2.setCreator("系统");
            templateDao.insertTemplate(ticketTemplateV2);
        } finally {
            // 是否当前线程持有锁
            if (lock.isHeldByCurrentThread()) {
                // 手动释放锁
                lock.unlock();
            }
        }
    }

    /**
     * 保存默认的详情模板
     *
     * @param orgId 组织id
     * @author jy.zhao
     * @date 2021/9/7 13:15
     **/
    private void saveDefaultDetailTemplate(String orgId) {
        TicketTemplateV2 detailTemplate = templateDao.getTemplateByName(orgId, Constants.TICKET_DETAIL_DEFAULT_TEMPLATE_NAME);
        if (Validator.isNotNull(detailTemplate)) {
            log.info("[ticket] 组织orgId={}下已有默认详情模板", orgId);
            return;
        }

        // 默认模板为null，加锁防止并发地新增
        String key = "ticket:template:" + orgId;
        RLock lock = this.redisService.getLock(key);
        // 加锁成功与否
        try {
            // 加锁, 不自动释放
            lock.lock(10L, TimeUnit.HOURS);
            detailTemplate = templateDao.getTemplateByName(orgId, Constants.TICKET_DETAIL_DEFAULT_TEMPLATE_NAME);
            if (Validator.isNotNull(detailTemplate)) {
                log.info("[ticket] 组织orgId={}下已有默认详情模板", orgId);
                return;
            }

            // 保存默认的详情模板的主要逻辑
            TicketTemplateV2 ticketTemplateV2 = new TicketTemplateV2();
            ticketTemplateV2.setOrgId(orgId);
            ticketTemplateV2.setName(Constants.TICKET_DETAIL_DEFAULT_TEMPLATE_NAME);
            ticketTemplateV2.setType(TemplateTypeEnum.TEMPLATE_INFO.getIndex());
            ticketTemplateV2.setUsed(1);
            // 默认模板序号为0
            ticketTemplateV2.setSeq(0);
            List<TicketTemplateV2.FieldInfo> fieldInfoList = new ArrayList<>();
            // 相关客户模板字段
            TicketTemplateV2.FieldInfo fileInfo = new TicketTemplateV2.FieldInfo();
            fileInfo.setName("相关客户");
            // 该字段可以修改
            fileInfo.setIsUpdate(TicketTemplateFieldUpdatedEnum.TEMPLATE_IS_UPDATE.getIndex());
            fileInfo.setType(TemplateFieldTypeEnum.SELECT.getIndex());
            fileInfo.setRequired(false);
            // 从field1开始增长，但处理模板的默认字段固定为field0
            fileInfo.setField("field1");
            // 单选的选项值
            TicketTemplateV2.SingleSelect singleSelect = new TicketTemplateV2.SingleSelect();
            singleSelect.setSource(TemplateSelectSourceEnum.SYSTEM_MEMBER.getIndex());
            singleSelect.setContent("联系人");
            // 详情描述模板字段
            fileInfo.setSingleSelect(singleSelect);
            TicketTemplateV2.FieldInfo fileInfo2 = new TicketTemplateV2.FieldInfo();
            fileInfo2.setName("详情描述");
            fileInfo2.setType(TemplateFieldTypeEnum.MULTI_LINE.getIndex());
            // 是否必填
            fileInfo2.setRequired(false);
            // 该字段可以修改
            fileInfo2.setIsUpdate(TicketTemplateFieldUpdatedEnum.TEMPLATE_IS_UPDATE.getIndex());
            // 从field1开始增长，但处理模板的默认字段固定为field0
            fileInfo2.setField("field2");
            fieldInfoList.add(fileInfo);
            fieldInfoList.add(fileInfo2);
            ticketTemplateV2.setFieldList(fieldInfoList);
            ticketTemplateV2.setCreator("系统");
            templateDao.insertTemplate(ticketTemplateV2);
        } finally {
            // 是否当前线程持有锁
            if (lock.isHeldByCurrentThread()) {
                // 手动释放锁
                lock.unlock();
            }
        }
    }
}
